from django.http import JsonResponse
from django.views.decorators.http import require_http_methods
from django.views.decorators.csrf import csrf_exempt
from django.contrib.auth.decorators import login_required
from django.conf import settings
from django.core.cache import cache
from django.utils import timezone
import os
import hashlib
import urllib.request
import urllib.parse
import json
import math


def _get_client_ip(request):
    xff = request.META.get('HTTP_X_FORWARDED_FOR')
    if xff:
        parts = [p.strip() for p in xff.split(',') if p.strip()]
        if parts:
            return parts[0]
    return request.META.get('REMOTE_ADDR') or ''


def _http_get(url, params):
    query = urllib.parse.urlencode(params)
    full = url + ('?' + query if query else '')
    with urllib.request.urlopen(full, timeout=5) as resp:
        body = resp.read().decode('utf-8')
        return json.loads(body)

def _build_sig(path: str, params: dict, sk: str) -> str:
    # 腾讯WebService签名：按键名升序拼接查询串，不包含sig本身
    items = sorted([(k, v) for k, v in params.items() if k != 'sig'], key=lambda x: x[0])
    query = urllib.parse.urlencode(items)
    raw = f"{path}?{query}{sk}"
    return hashlib.md5(raw.encode('utf-8')).hexdigest()


def _haversine(lat1, lng1, lat2, lng2):
    R = 6371000.0
    phi1 = math.radians(lat1)
    phi2 = math.radians(lat2)
    dphi = math.radians(lat2 - lat1)
    dlambda = math.radians(lng2 - lng1)
    a = math.sin(dphi/2)**2 + math.cos(phi1)*math.cos(phi2)*math.sin(dlambda/2)**2
    c = 2 * math.atan2(math.sqrt(a), math.sqrt(1-a))
    return R * c


def _analyze_error_status(status_code, message):
    """分析腾讯地图API错误码并提供详细说明"""
    error_mapping = {
        110: "请求来源未被授权 - 请检查key是否有效、是否启用逆地址解析服务、或域名未授权",
        311: "请求参数信息有误 - 缺少location或key参数",
        310: "请求参数信息有误 - location参数格式不正确或坐标超出范围", 
        312: "请求参数信息有误 - 传递了未知参数",
        320: "坐标参数解析错误 - 坐标转换失败",
        121: "签名验证失败 - 请检查签名计算是否正确",
        191: "请求超时或服务异常"
    }
    return error_mapping.get(status_code, f"未知错误(status={status_code}): {message}")


@csrf_exempt
@require_http_methods(["GET"])
@login_required
def self_location_view(request):
    try:
        key = getattr(settings, 'TENCENT_MAP_KEY', '') or os.environ.get('TENCENT_MAP_KEY', '')
        if not key:
            return JsonResponse({
                'success': False, 
                'message': '未配置腾讯位置服务Key，请在环境变量中设置TENCENT_MAP_KEY'
            }, status=500)
        ip = _get_client_ip(request)
        data = _http_get('https://apis.map.qq.com/ws/location/v1/ip', {'ip': ip, 'key': key})
        status_code = int(data.get('status', -1))
        if status_code != 0:
            error_message = data.get('message', 'IP定位失败')
            detailed_error = _analyze_error_status(status_code, error_message)
            return JsonResponse({
                'success': False, 
                'message': detailed_error,
                'error_code': status_code,
                'original_message': error_message
            }, status=500)
        loc = data.get('result', {}).get('location', {})
        ad = data.get('result', {}).get('ad_info', {})
        # 保存到用户档案，便于后台管理
        try:
            profile = request.user.profile
            profile.lat = loc.get('lat')
            profile.lng = loc.get('lng')
            profile.city = ad.get('city')
            profile.province = ad.get('province')
            profile.nation = ad.get('nation')
            profile.location_updated_at = timezone.now()
            profile.save(update_fields=['lat','lng','city','province','nation','location_updated_at'])
        except Exception:
            pass
        return JsonResponse({'success': True, 'data': {
            'ip': ip,
            'lat': loc.get('lat'),
            'lng': loc.get('lng'),
            'city': ad.get('city'),
            'province': ad.get('province'),
            'nation': ad.get('nation')
        }})
    except Exception as e:
        return JsonResponse({'success': False, 'message': f'获取定位失败: {str(e)}'}, status=500)


@csrf_exempt
@require_http_methods(["GET"])
@login_required
def reverse_geocode_view(request):
    try:
        key = getattr(settings, 'TENCENT_MAP_KEY', '') or os.environ.get('TENCENT_MAP_KEY', '')
        if not key:
            return JsonResponse({
                'success': False, 
                'message': '未配置腾讯位置服务Key，请在环境变量中设置TENCENT_MAP_KEY'
            }, status=500)
        lat = float(request.GET.get('lat'))
        lng = float(request.GET.get('lng'))
        nlat = round(lat, 5)
        nlng = round(lng, 5)
        cache_key = f"revgeo:{nlat}:{nlng}"
        cached = cache.get(cache_key)
        if cached:
            return JsonResponse({'success': True, 'data': cached})
        params = {'location': f"{lat},{lng}", 'key': key}
        # 使用域名白名单，禁用签名验证
        sig_secret = getattr(settings, 'TENCENT_MAP_SIG_SECRET', '') or os.environ.get('TENCENT_MAP_SIG_SECRET', '')
        
        # 检查是否禁用了签名验证
        if sig_secret and sig_secret != 'DISABLE':
            params['sig'] = _build_sig('/ws/geocoder/v1', params, sig_secret)
        
        # 调用腾讯地图API
        try:
            data = _http_get('https://apis.map.qq.com/ws/geocoder/v1', params)
        except Exception as e:
            return JsonResponse({'success': False, 'message': f'API调用失败: {str(e)}'}, status=500)
        if int(data.get('status', -1)) != 0:
            # 更新签名设置
            sig_secret = getattr(settings, 'TENCENT_MAP_SIG_SECRET', '') or os.environ.get('TENCENT_MAP_SIG_SECRET', '')
            translate_params = {'locations': f"{lat},{lng}", 'type': 1, 'key': key}
            if sig_secret and sig_secret != 'DISABLE':
                translate_params['sig'] = _build_sig('/ws/coord/v1/translate', translate_params, sig_secret)
            tdata = _http_get('https://apis.map.qq.com/ws/coord/v1/translate', translate_params)
            tstatus_code = int(tdata.get('status', -1))
            if tstatus_code != 0:
                t_error_message = tdata.get('message', '坐标转换失败')
                t_detailed_error = _analyze_error_status(tstatus_code, t_error_message)
                return JsonResponse({
                    'success': False, 
                    'message': t_detailed_error,
                    'error_code': tstatus_code,
                    'original_message': t_error_message
                }, status=400)
            tres = tdata.get('locations') or tdata.get('result') or []
            try:
                t0 = tres[0]
                tlat = float(t0.get('lat'))
                tlng = float(t0.get('lng'))
            except Exception:
                return JsonResponse({'success': False, 'message': '坐标转换失败'}, status=500)
            nlat = round(tlat, 5)
            nlng = round(tlng, 5)
            cache_key = f"revgeo:{nlat}:{nlng}"
            cached = cache.get(cache_key)
            if cached:
                return JsonResponse({'success': True, 'data': cached})
            params = {'location': f"{tlat},{tlng}", 'key': key}
            if sig_secret and sig_secret != 'DISABLE':
                params['sig'] = _build_sig('/ws/geocoder/v1', params, sig_secret)
            data = _http_get('https://apis.map.qq.com/ws/geocoder/v1', params)
        status_code = int(data.get('status', -1))
        if status_code != 0:
            error_message = data.get('message', '逆地理解析失败')
            detailed_error = _analyze_error_status(status_code, error_message)
            return JsonResponse({
                'success': False, 
                'message': detailed_error,
                'error_code': status_code,
                'original_message': error_message
            }, status=400)
        result = data.get('result', {})
        payload = {
            'address': result.get('address'),
            'formatted_addresses': result.get('formatted_addresses'),
            'ad_info': result.get('ad_info'),
        }
        cache.set(cache_key, payload, timeout=7*24*3600)
        return JsonResponse({'success': True, 'data': payload})
    except (TypeError, ValueError):
        return JsonResponse({'success': False, 'message': '缺少或非法的坐标参数'}, status=400)
    except Exception as e:
        return JsonResponse({'success': False, 'message': f'逆地理解析失败: {str(e)}'}, status=500)


@csrf_exempt
@require_http_methods(["GET"])
@login_required
def distance_view(request):
    try:
        def parse_pair(s):
            lat, lng = [float(x) for x in (s or '').split(',')]
            return lat, lng
        if request.GET.get('from') and request.GET.get('to'):
            lat1, lng1 = parse_pair(request.GET.get('from'))
            lat2, lng2 = parse_pair(request.GET.get('to'))
        else:
            lat1 = float(request.GET.get('from_lat'))
            lng1 = float(request.GET.get('from_lng'))
            lat2 = float(request.GET.get('to_lat'))
            lng2 = float(request.GET.get('to_lng'))
        meters = _haversine(lat1, lng1, lat2, lng2)
        return JsonResponse({'success': True, 'data': {
            'distance_meters': int(round(meters)),
            'distance_km': round(meters/1000.0, 3)
        }})
    except (TypeError, ValueError):
        return JsonResponse({'success': False, 'message': '坐标参数错误'}, status=400)
    except Exception as e:
        return JsonResponse({'success': False, 'message': f'距离计算失败: {str(e)}'}, status=500)